本文同步更新於blog
前情提要:鐵路運輸系統,參考範例:運輸系統(簡單工廠模式)
<?php
namespace App\FactoryPattern\Transport\SimpleFactoryPattern;
use App\FactoryPattern\Transport\SimpleFactoryPattern\RailwayModelFactory;
class Program
{
    /**
     * @param string $model
     * @return string
     */
    public function getModel($model)
    {
        $railwayModelFactory = new RailwayModelFactory();
        $model = $railwayModelFactory->createModel($model);
        return $model->getName();
    }
}
需求一:隨著公司擴大,運輸系統不再僅限鐵路,將引進波音747,進軍航空業
此時發現,原本的簡單工廠,
從創建對象都是火車,到有飛機,
邏輯越來越複雜了。
常常違反開放封閉原則,
讓我們用工廠方法模式修正它。
<?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern\Contracts;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\Model;
interface ModelFactory
{
    public function createModel(): Model;
}
<?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\Model;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\FactoryMethodPattern\Model\LocalTrain;
class LocalTrainFactory implements ModelFactory
{
    public function createModel(): Model
    {
        //取得生產材料...
        //招募技術團隊...
        return new LocalTrain();
    }
}
<?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\Model;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\FactoryMethodPattern\Model\SemiExpress;
class SemiExpressFactory implements ModelFactory
{
    public function createModel(): Model
    {
        //取得生產材料...
        //招募技術團隊...
        return new SemiExpress();
    }
}
<?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\Model;
use App\FactoryPattern\Transport\FactoryMethodPattern\Model\LimitedExpress;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\ModelFactory;
class LimitedExpressFactory implements ModelFactory
{
    public function createModel(): Model
    {
        //取得生產材料...
        //招募技術團隊...
        return new LimitedExpress();
    }
}
<?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern;
use App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories\LimitedExpressFactory;
use App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories\LocalTrainFactory;
use App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories\SemiExpressFactory;
class Program
{
    /**
     * @param string $model
     * @return string
     */
    public function getModel($model)
    {
        $modelFactory = $this->createModelFactory($model);
        $model = $modelFactory->createModel();
        return $model->getName();
    }
    /**
     * @param string $model
     * @return ModelFactory
     */
    private function createModelFactory($model)
    {
        switch ($model) {
            case 'LimitedExpress':
                return new LimitedExpressFactory();
                break;
            case 'LocalTrain':
                return new LocalTrainFactory();
                break;
            case 'SemiExpress':
                return new SemiExpressFactory();
                break;
        }
    }
}
<?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\ModelFactory;
use ReflectionClass;
class Program
{
    /**
     * @param string $model
     * @return string
     */
    public function getModel($model)
    {
        $modelFactory = $this->createModelFactory($model);
        $model = $modelFactory->createModel();
        return $model->getName();
    }
    /**
     * @param string $model
     * @return ModelFactory
     */
    private function createModelFactory($model)
    {
        $namespace = 'App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories';
        $className = $model . 'Factory';
        $reflector = new ReflectionClass($namespace . '\\' . $className);
        return $reflector->newInstance();
    }
}
這下子,改造工程結束,讓我們回到波音747!
<?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern\Model;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\Model;
class Boeing747 implements Model
{
    public function getName()
    {
        return '波音747';
    }
}
<?php
namespace App\FactoryPattern\Transport\FactoryMethodPattern\ModelFactories;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\FactoryMethodPattern\Contracts\Model;
use App\FactoryPattern\Transport\FactoryMethodPattern\Model\Boeing747;
class Boeing747Factory implements ModelFactory
{
    public function createModel(): Model
    {
        return new Boeing747();
    }
}
[單一職責原則]
我們將創建類別 (Creator)與產品類別 (Product)視作兩種不同的職責。
[開放封閉原則]
當新增新的產品時,我們僅需新增產品類別及新增創建類別。
當修改既有產品時,我們僅需修改其產品類別,不會影響到其他的產品類別。
[介面隔離原則]
拆分出工廠介面與機型介面。
[依賴反轉原則]
客戶依賴於抽象工廠介面與機型介面。
工廠類別實作抽象的工廠介面。
機型類別實作抽象的機型介面。
最後附上類別圖:
(註:若不熟悉 UML 類別圖,可參考UML類別圖說明。)
ʕ •ᴥ•ʔ:不久之後,我們會再一次看到這個範例,敬請期待。